home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d18 / vis082s.arc / MODEM2.PAS < prev    next >
Pascal/Delphi Source File  |  1991-04-17  |  27KB  |  770 lines

  1. {$R-,S-,I-,D-,F+,V-,B-,N-,L+}
  2.  
  3.  
  4. { ViSiON Modem driver routines}
  5. { Copyright 1990 ViSiON Staff }
  6.  
  7. unit modem;
  8.  
  9. Interface
  10.  
  11. uses crt,
  12.      dos,
  13.      gentypes;
  14.  
  15.   Const
  16.   YasyncVersion = 14; { Current version * 10 }
  17.   MaxPorts = 4;       { Maximum number of ports supported }
  18.   DefaultBufferSize = 512; { Default size of receive & transmit buffers was 2048}
  19.   BaudRateDividend : LongInt = 115200; { Used to compute baud rate divisor }
  20.   TimeoutMilliseconds : LongInt = 1000; { Retry Send until timeout ms }
  21.   BreakMiliseconds : Word = 300; { Duration of break signal }
  22.  
  23.   { These constants define the bits for the Line Status Register }
  24.   LSRRcvReady = $01;  { Received data ready }
  25.   LSROverrun  = $02;  { OverRun error }
  26.   LSRParity   = $04;  { Parity error }
  27.   LSRFrame    = $08;  { Framing error }
  28.   LSRBreak    = $10;  { Break detected }
  29.   LSRXhReady  = $20;  { Transmit hold register empty }
  30.   LSRXsReady  = $40;  { Transmit shift register empty }
  31.   LSRTimeout  = $80;  { Time out (software implemented) }
  32.  
  33.   { These constants define the bits for the Modem Status Register }
  34.   MSRctsDelta = $01;  { Clear To Send changed }
  35.   MSRdsrDelta = $02;  { DataSet Ready changed }
  36.   MSRriDelta  = $04;  { Ring Indicate changed }
  37.   MSRcdDelta  = $08;  { Carrier Detect changed }
  38.   MSRcts      = $10;  { Clear To Send }
  39.   MSRdsr      = $20;  { DataSet Ready }
  40.   MSRri       = $40;  { Ring Indicate }
  41.   MSRcd       = $80;  { Carrier Detect }
  42.  
  43. Type
  44.   AsyncBuffer = Array[0..32767] Of Byte;
  45.   {
  46.     Note, AsyncBuffer is for the buffer pointer declarations below.  The actual
  47.     buffer size is specified in Async_Control.
  48.   }
  49.   Async_Control = Record
  50.   {
  51.     This record contains control information used to manage the activity of
  52.     a port.  Certain fields may be altered before calling OpenPort.  These
  53.     are marked with an "*" in the comments.  For instance...
  54.  
  55.       With AsyncPort[2] Do Begin
  56.         ReceiveSize := 10000;
  57.         TransmitSize := 8000;
  58.         WaitForXon := True;
  59.         XoHand := True;
  60.         XoTransparent := False;
  61.       End;
  62.       OpenPort(usecom, 9600, 7, 1, 'E');
  63.  
  64.     ...sets buffer sizes and Xon/Xoff handshaking for COM2 and opens COM2 at
  65.     9600 bps, 7 bits, 1 stop bit, even parity.  Fields not marked with "*"
  66.     in comments are used internally by YAsync; do not alter these fields.
  67.     Do not alter any fields while a port is open.
  68.   }
  69.     PortOpen : Boolean;       { True if port is currently open }
  70.     VectorIndex : Byte;       { Index to interrupt vector save area }
  71.  
  72.     IrqNumber : Byte;         { *IRQ number }
  73.     IntNumber : Byte;         { *Interrupt number }
  74.     BasePort : Word;          { *Base I/O port for UART }
  75.     {
  76.       The IRQ, Interrupt and base port numbers are set to default values during
  77.       initialization.  The defaults (see implementation Const's for details)
  78.       should be appropriate for most systems, but they may be reset if
  79.       necessary before calling OpenPort.
  80.     }
  81.     LineStatus : Byte;        { Line status register for ErrorRoutine,
  82.                                   Decode with LSRxxx constants above }
  83.     ModemStatus : Byte;       { Modem status register for ModemRoutine,
  84.                                   Decode with MSRxxx constants above }
  85.     UserData : Word;          { This field is unused by YAsync routines }
  86.  
  87.     WaitForXon : Boolean;     { *Inhibit transmit between Xoff and Xon }
  88.     WaitForCts : Boolean;     { *Inhibit transmit if not Cts }
  89.     WaitForDsr : Boolean;     { *Inhibit transmit if not Dsr }
  90.     WaitForCd  : Boolean;     { *Inhibit transmit if not Cd }
  91.     XoHand : Boolean;         { *Handshake receive buffer with Xon/Xoff }
  92.     RtsHand : Boolean;        { *Handshake receive buffer with Rts }
  93.     DtrHand : Boolean;        { *Handshake receive buffer with Cts }
  94.     XoTransparent : Boolean;  { *Pass Xon/Xoff through to data stream }
  95.     {
  96.       If XoTransparent is False, Xon and Xoff characters are not placed in
  97.       the receive buffer (they will still have their handshaking effect if
  98.       WaitForXon is True).  The defaults are:
  99.         WaitForXon = False
  100.         WaitForCts = False
  101.         WaitForDsr = False
  102.         WaitForCd = False
  103.         XoHand = False
  104.         RtsHand = True
  105.         DtrHand = false
  106.         XoTransparent = True
  107.     }
  108.     TransmitEnabled : Boolean;{ If False, transmit is inhibited }
  109.     SenderEnabled : Boolean;  { Handshake signal was sent to sender }
  110.     AwaitingXon : Boolean;    { True if waiting for Xon }
  111.     AwaitingCts : Boolean;    { True if waiting for Cts }
  112.     AwaitingDsr : Boolean;    { True if waiting for Dsr }
  113.     AwaitingCd  : Boolean;    { True if waiting for Cd }
  114.     AwaitingCh  : Boolean;    { True if waiting for character to transmit }
  115.     StreamInsert : Byte;      { Character to be forced into output stream }
  116.  
  117.     ErrorRoutine : Pointer;   { *Pointer to routine for line status interrupt }
  118.     ModemRoutine : Pointer;   { *Pointer to routine for modem status intrpt }
  119.     {
  120.       These routines must be declared as Far-calls ($F+) at the global level
  121.       with a one-Word value parameter, the port number.  Do NOT declare
  122.       them as "Interrupt" type procedures.  These routines, though not
  123.       "Interrupt" type routines, are called from the interrupt service routine,
  124.       therefore they should follow the same rules as an ISR - no DOS services,
  125.       reentrant, etc.  ErrorRoutine should examine LineStatus to determine the
  126.       cause of the error; ModemRoutine should examine ModemStatus.
  127.     }
  128.     ReceiveBuffer : ^AsyncBuffer;  { *Receive buffer }
  129.     ReceiveSize : Word;            { *0..32767 }
  130.     ReceiveHead : Word;
  131.     ReceiveTail : Word;
  132.     TransmitBuffer : ^AsyncBuffer; { *Transmit buffer }
  133.     TransmitSize : Word;           { *0..32767 }
  134.     TransmitHead : Word;
  135.     TransmitTail : Word;
  136.     ReleaseReceive : Boolean; { YAsync obtained receive buffer, must release }
  137.     ReleaseTransmit : Boolean;{ Ditto, transmit buffer }
  138.     {
  139.       Buffers are allocated from the heap if the corresponding pointer is Nil
  140.       when OpenPort is called.  You may allocate a buffer yourself and place
  141.       its address in ReceiveBuffer or TransmitBuffer, and its size in
  142.       ReceiveSize or TransmitSize.  Alternatively, you may change the size of
  143.       the automatically allocated buffer by changing ReceiveSize or
  144.       TransmitSize before calling OpenPort.
  145.     }
  146.   End;
  147.  
  148.   SetOfChar = Set of Char; { Used by LineReadPort below }
  149.  
  150. Var
  151.   comport:integer;
  152.   AsyncPort: Array[1..MaxPorts] Of Async_Control;
  153.   PortOpenError : Byte;  { Error code from open routine..
  154.                            0 Normal, open successful
  155.                            1 Port number out of range (1..4)
  156.                            2 Baud rate out of range (50..115200)
  157.                            3 Word length out of range (5..8)
  158.                            4 Stop bits out of range (1..2)
  159.                            5 Invalid parity (N,E,O,1,0)
  160.                            6 Buffer size invalid (2..32767)
  161.                            7 Insufficient heap space for buffers
  162.                            8 UART not responding
  163.                            9 Program bug - should never happen
  164.                          }
  165.  
  166.  
  167.   procedure sendbreak;
  168.   procedure initializeAsync;
  169.   Procedure closeport;
  170.   procedure AsyncExit;
  171.   Procedure sendchar(ch:Char);
  172.   Function numchars:word;
  173.   Function getchar:Char;
  174.   Procedure hangup;
  175.   Procedure setparam(Comp,baud:word;parity:boolean);
  176.   Function carrier:Boolean;
  177.   Procedure setterminalready(dtr:Boolean);
  178.   Procedure dontanswer;
  179.   Procedure doanswer;
  180.   function transmitbufferused:word;
  181.   Procedure cleartransmitbuffer;
  182.   Procedure clearreceivebuffer;
  183.  
  184. Implementation
  185.  
  186.  
  187. Const
  188.   { These are the offsets from BasePort of the 8250 control registers }
  189.   DLL = 0; { Divisor Latch Least-significant-byte (LCR bit $80 on) }
  190.   DLM = 1; { Divisor Latch Most-significant-byte (LCR bit $80 on) }
  191.   RBR = 0; { Receiver Buffer Register (read) }
  192.   THR = 0; { Transmitter Holding Register (write) }
  193.   IER = 1; { Interrupt Enable Register }
  194.   IIR = 2; { Interrupt Identification Register (read only) }
  195.   LCR = 3; { Line Control Register }
  196.   MCR = 4; { Modem Control Register }
  197.   LSR = 5; { Line Status Register }
  198.   MSR = 6; { Modem Status Register }
  199.  
  200.   { These constants define the bits for the Modem Control Register }
  201.   MCRloop     = $10; { Loopback mode }
  202.   MCRout2     = $08; { Out2, must be on for interrupts }
  203.   MCRout1     = $04; { Out1 ? }
  204.   MCRrts      = $02; { Request to send }
  205.   MCRdtr      = $01; { Data terminal ready }
  206.  
  207.   { These are the default base ports, IRQs and interrupts }
  208.   BasePorts : Array[1..MaxPorts] Of Word = ($03F8,$02F8,$03E8,$02E8);
  209.   IRQs : Array[1..MaxPorts] Of Byte = (4,3,4,3);
  210.   Interrupts : Array[1..MaxPorts] Of Byte = (12,11,12,11);
  211.  
  212.   XOn  = 17; {^Q, DC1, XOn }
  213.   XOff = 19; {^S, DC3, XOff }
  214.  
  215. Type
  216.   VectorType = Record
  217.     UseCount : Byte;  { Number of ports using this interrupt vector }
  218.     IntrptNo : Byte;  { Interrupt number for this vector }
  219.     Vector : Pointer; { Old value of vector }
  220.     NextPort : Word;  { Next port to process }
  221.     PortList : Array[0..MaxPorts] Of Word; { Open ports using this vector }
  222.   End;
  223.  
  224. Var
  225.   FormerExitProc : Pointer; { Save area for ExitProc pointer }
  226.   VectorSave : Array[1..MaxPorts] Of VectorType;
  227.  
  228.   Procedure CallFar(ComPort:Word;proc:Pointer);
  229.       Inline($5B/{ pop bx   ; save @proc in cx:bx }
  230.         $59/{ pop cx }
  231.         $0E/{ push cs  ; set up return address }
  232.         $E8/$00/$00/{ call $ }
  233.         $58/{ pop ax }
  234.         $05/$08/$00/{ add ax,8 }
  235.         $50/{ push ax }
  236.         $51/{ push cx  ; restore @proc to stack }
  237.         $53/{ push bx }
  238.         $CB);{ retf     ; go to proc }
  239.  
  240.   Procedure DisableInterrupts; { Disable 80x86/8 interrupts }
  241.         Inline($FA);
  242.  
  243.   Procedure EnableInterrupts; { Enable 80x86/8 interrupts }
  244.       Inline($FB);
  245.  
  246.   Procedure SendBreak; { Send break signal }
  247.   Var Timer : LongInt;
  248.       LastTail : Word;
  249.   Begin { SendBreak }
  250.     With AsyncPort[ComPort] Do Begin
  251.       If TransmitSize > 0 Then Begin { Allow transmit buffer to empty }
  252.     Timer := TimeoutMilliseconds;
  253.     LastTail := TransmitTail;
  254.     While (TransmitHead <> TransmitTail) And (Timer > 0) Do Begin
  255.       Dec(Timer);
  256.       Delay(1);
  257.       If LastTail <> TransmitTail Then Begin
  258.         LastTail := TransmitTail;
  259.         Timer := TimeoutMilliseconds;
  260.       End;
  261.     End;
  262.     If Timer = 0 Then Begin
  263.       LineStatus := LSRTimeout;
  264.       If ErrorRoutine <> Nil Then CallFar(ComPort, ErrorRoutine);
  265.     End;
  266.       End;
  267.       Port[BasePort+LCR] := Port[BasePort+LCR] Or $40; { Turn on break }
  268.       Delay(BreakMiliseconds);
  269.       Port[BasePort+LCR] := Port[BasePort+LCR] And $BF; { Turn off break }
  270.     End;
  271.   End;  { SendBreak }
  272.  
  273.   Function numchars:Word; { Return number of receive buffer bytes used }
  274.   Begin
  275.     With AsyncPort[ComPort] Do Begin
  276.       DisableInterrupts;
  277.       If ReceiveHead<ReceiveTail Then numchars:=(ReceiveSize-ReceiveTail)+ReceiveHead
  278.     Else numchars:=ReceiveHead-ReceiveTail;
  279.       EnableInterrupts;
  280.     End;
  281.   End;
  282.  
  283.   Function TransmitBufferUsed:Word;
  284.    { Return number of transmit buffer bytes used }
  285.   Begin
  286.     With AsyncPort[ComPort] Do Begin
  287.       DisableInterrupts;
  288.       If TransmitHead<TransmitTail Then TransmitBufferUsed:=(TransmitSize-TransmitTail)+TransmitHead
  289.     Else TransmitBufferUsed:=TransmitHead-TransmitTail;
  290.       EnableInterrupts;
  291.     End;
  292.   End;{ TransmitBufferUsed }
  293.  
  294.   Procedure SetTerminalReady(Dtr:Boolean); { Set DTR on/off }
  295.   Begin
  296.     If transmitbufferused>0 Then Delay(1000);
  297.     If transmitbufferused>0 Then Delay(1000);
  298.     If transmitbufferused>0 Then Delay(1000);
  299.     With AsyncPort[ComPort] Do Begin
  300.       If Dtr Then Port[BasePort+MCR]:=Port[BasePort+MCR] Or MCRdtr
  301.     Else Port[BasePort+MCR]:=Port[BasePort+MCR] And Not MCRdtr;
  302.     End;
  303.   End;
  304.  
  305.   Procedure dontanswer;
  306.   Begin
  307.     setterminalready(False);
  308.   End;
  309.  
  310.   Procedure doanswer;
  311.   Begin
  312.     setterminalready(True);
  313.   End;
  314.  
  315.   Procedure hangup;
  316.   Begin
  317.     Repeat Until TransmitBufferUsed=0;
  318.     if carrier then delay(3000);
  319.     dontanswer;
  320.   End;
  321.  
  322.   Procedure SetRTS(Rts:Boolean); { Set RTS on/off }
  323.   Begin
  324.     With AsyncPort[ComPort] Do Begin
  325.       If Rts Then Port[BasePort+MCR]:=Port[BasePort+MCR] Or MCRrts
  326.     Else Port[BasePort+MCR]:=Port[BasePort+MCR] And Not MCRrts;
  327.     End;
  328.   End;{ SetRTS }
  329.  
  330.   Procedure EnableTransmit; { Enable buffered transmit, restart interrupt if necessary }
  331.   Begin
  332.     With AsyncPort[ComPort] Do Begin
  333.       TransmitEnabled:=True;
  334.       DisableInterrupts;
  335.       If (TransmitHead<>TransmitTail) And AwaitingCh Then Begin
  336.     Port[BasePort+THR]:=TransmitBuffer^[TransmitTail];
  337.     TransmitTail:=Succ(TransmitTail);
  338.     If TransmitTail=TransmitSize Then TransmitTail:=0;
  339.       End;
  340.       EnableInterrupts;
  341.     End;
  342.   End;
  343.  
  344.   Procedure EnableSender; { Enable sender via handshaking signal }
  345.   Begin
  346.     With AsyncPort[ComPort] Do Begin
  347.       If Not SenderEnabled Then Begin
  348.     If XoHand Then Begin
  349.       DisableInterrupts;
  350.       If AwaitingCh Then Port[BasePort+THR]:=XOn
  351.         Else StreamInsert:=XOn;
  352.       EnableInterrupts;
  353.     End;
  354.     If DtrHand Then SetTerminalready(True);
  355.     If RtsHand Then SetRts(True);
  356.     SenderEnabled:=True;
  357.       End;
  358.     End;
  359.   End;{ EnableSender }
  360.  
  361.   Procedure DisableSender; { Disable sender via handshaking signal }
  362.   Begin
  363.     With AsyncPort[ComPort] Do Begin
  364.       If SenderEnabled Then Begin
  365.     If XoHand Then Begin
  366.       DisableInterrupts;
  367.       If AwaitingCh Then Port[BasePort+THR]:=XOff
  368.         Else StreamInsert:=XOff;
  369.       EnableInterrupts;
  370.     End;
  371.     If DtrHand Then SetTerminalready(False);
  372.     If RtsHand Then SetRts(False);
  373.     SenderEnabled:=False;
  374.       End;
  375.     End;
  376.   End;{ DisableSender }
  377.  
  378.   Procedure ClearTransmitBuffer; { Discard all unsent characters in the transmit buffer. }
  379.   Begin
  380.     With AsyncPort[ComPort] Do Begin
  381.       DisableInterrupts;
  382.       TransmitHead:=0;
  383.       TransmitTail:=0;
  384.       EnableInterrupts;
  385.     End;
  386.   End;{ ClearTransmitBuffer }
  387.  
  388.   Procedure ClearReceiveBuffer; { Discard all unsent characters in the receive buffer. }
  389.   Begin
  390.     With AsyncPort[ComPort] Do Begin
  391.       DisableInterrupts;
  392.       ReceiveHead:=0;
  393.       ReceiveTail:=0;
  394.       EnableInterrupts;
  395.       EnableSender;
  396.     End;
  397.   End;
  398.  
  399.   Procedure AsyncISR(VectorNo:Word);
  400.   Var i,Next:Word;
  401.       work:Byte;
  402.       done:Boolean;
  403.   Begin
  404.     EnableInterrupts;
  405.     With VectorSave[VectorNo] Do Begin
  406.       Inc(NextPort);
  407.       If NextPort>UseCount Then NextPort:=1;
  408.       i:=NextPort;
  409.       Repeat
  410.     ComPort:=PortList[i];
  411.     With AsyncPort[ComPort] Do Begin
  412.       done:=False;
  413.       Repeat
  414.         Case Port[BasePort+IIR] Of
  415.           $06:Begin{ Received character error or break }
  416.             LineStatus:=Port[BasePort+LSR];
  417.             If (LineStatus And LSRBreak)<>0 Then Begin
  418.               LineStatus:=LineStatus And Not LSRFrame;
  419.               work:=Port[BasePort+RBR];
  420.             End;
  421.             If ErrorRoutine<>Nil Then CallFar(ComPort,ErrorRoutine);
  422.           End;
  423.           $04:Begin{ Received data ready }
  424.             work:=Port[BasePort+RBR];
  425.             If XoTransparent Or ((work<>XOff) And (work<>XOn)) Then Begin
  426.               next:=Succ(ReceiveHead);
  427.               If next=ReceiveSize Then next:=0;
  428.               If next=ReceiveTail Then Begin
  429.             LineStatus:=LSROverrun;
  430.             If ErrorRoutine<>Nil Then CallFar(ComPort,ErrorRoutine);
  431.               End Else Begin
  432.             ReceiveBuffer^[ReceiveHead]:=work;
  433.             ReceiveHead:=next;
  434.               End;
  435.               If (XoHand Or RtsHand Or DtrHand) And SenderEnabled Then Begin
  436.             If ReceiveHead<ReceiveTail Then next:=(ReceiveSize-ReceiveTail)+ReceiveHead
  437.               Else next:=ReceiveHead-ReceiveTail;
  438.             If next>(ReceiveSize-(ReceiveSize Shr 2)) Then DisableSender;
  439.               End;
  440.             End;
  441.           End;
  442.  
  443.           $02:Begin{ Transmit holding register empty }
  444.             If StreamInsert>0 Then Begin
  445.               Port[BasePort+THR]:=StreamInsert;
  446.               StreamInsert:=0;
  447.             End Else If (TransmitHead<>TransmitTail) And TransmitEnabled Then Begin
  448.               Port[BasePort+THR]:=TransmitBuffer^[TransmitTail];
  449.               Inc(TransmitTail);
  450.               If TransmitTail=TransmitSize Then TransmitTail:=0;
  451.             End Else AwaitingCh:=True;
  452.           End;
  453.  
  454.           $00:Begin{ Modem status change }
  455.             ModemStatus:=Port[BasePort+MSR];
  456.             AwaitingCts:=WaitForCts And ((ModemStatus And MSRcts)=0);
  457.             AwaitingDsr:=WaitForDsr And ((ModemStatus And MSRdsr)=0);
  458.             AwaitingCd:=WaitForCd And ((ModemStatus And MSRcd)=0);
  459.             If (AwaitingCts Or AwaitingDsr Or AwaitingCd Or AwaitingXon) Then TransmitEnabled:=False
  460.               Else If Not TransmitEnabled Then EnableTransmit;
  461.             If ModemRoutine<>Nil Then CallFar(ComPort,ModemRoutine);
  462.           End;
  463.           Else done:=True;
  464.         End;
  465.       Until done;
  466.     End;
  467.     Inc(i);
  468.     If i>UseCount Then i:=1;
  469.       Until i=NextPort;
  470.     End;
  471.     DisableInterrupts;
  472.     Port[$20]:=$20;{ Non-specific EOI to 8259 }
  473.   End;
  474.  
  475.   Procedure AsyncISR1;Interrupt;Begin AsyncISR(1);End;
  476.   Procedure AsyncISR2;Interrupt;Begin AsyncISR(2);End;
  477.   Procedure AsyncISR3;Interrupt;Begin AsyncISR(3);End;
  478.   Procedure AsyncISR4;Interrupt;Begin AsyncISR(4);End;
  479.  
  480.   Procedure ClosePort; { Release async port }
  481.   Var Timer:LongInt;
  482.       i,LastTail:Word;
  483.   Begin
  484.     With AsyncPort[ComPort] Do Begin
  485.       If PortOpen Then Begin
  486. (*    { Allow transmit buffer to empty }
  487.     Timer := TimeoutMilliseconds;
  488.     LastTail := TransmitTail;
  489.     While nd (Timer > 0) Do Begin
  490.       Dec(Timer);
  491.       Delay(1);
  492.       If LastTail <> TransmitTail Then Begin
  493.         LastTail := TransmitTail;
  494.         Timer := TimeoutMilliseconds;
  495.       End;
  496.     End;
  497.     If Timer = 0 Then Begin
  498.       LineStatus := LSRTimeout;
  499.       If ErrorRoutine <> Nil Then CallFar(ComPort, ErrorRoutine);
  500.     End; *)
  501.     With VectorSave[VectorIndex] Do Begin
  502.       i:=0;
  503.       Repeat Inc(i) Until (i>=UseCount) Or (PortList[i]=ComPort);
  504.       PortList[i]:=PortList[UseCount];
  505.       Dec(UseCount);
  506.       If UseCount=0 Then Begin{ No more ports using this irq }
  507.         Port[$21] := Port[$21] Or (1 Shl IrqNumber);
  508.         SetIntVec(IntrptNo,Vector);
  509.       End;
  510.     End;
  511.     If ReleaseReceive Then Begin { Free buffers }
  512.       FreeMem(ReceiveBuffer,ReceiveSize);
  513.       ReceiveBuffer:=Nil;
  514.     End;
  515.     If ReleaseTransmit Then Begin
  516.       FreeMem(TransmitBuffer,TransmitSize);
  517.       TransmitBuffer:=Nil;
  518.     End;
  519.     PortOpen:=False;
  520.     port[$20]:=$20;
  521.     Port[BasePort+IER]:=0;
  522.     Port[BasePort+MCR] := port [baseport+mcr] or McRdTr;
  523.       End;
  524.     End;
  525.   End;
  526.  
  527.   Function OpenPort(ComPort:Word;{ Com 1-4 }
  528.                           BaudRate:LongInt;{ BPS, 50..115200 }
  529.                           WordLength:Word;{ 5..8 bits }
  530.                           StopBits:Word;{ 1..2 stop bits }
  531.                           Parity:Char{ N,E,O,1,0 }
  532.                           ):Boolean;{ Return True if open successful }
  533.   Var BaudDivisor:Word;
  534.       Work,FreeSave:Byte;
  535.   Begin
  536.     If (ComPort<1) Or (ComPort>MaxPorts) Then PortOpenError:=1
  537.      Else With AsyncPort[ComPort] Do Begin
  538.       If PortOpen Then ClosePort Else Begin{ Precautionary... }
  539.         port[$20]:=$20;
  540.         Port[BasePort+IER]:=0;
  541.         Port[BasePort+MCR] := port [baseport+mcr] or McRdTr;
  542.       End;
  543.       PortOpenError:=0;
  544.       Parity:=UpCase(Parity);
  545.       if (receivesize<16) or (receivesize>2048) then receivesize:=64;
  546.       if (transmitsize<16) or (transmitsize>2048) then transmitsize:=512;
  547.       If (BaudRate<50) Or (BaudRate>115200) Then PortOpenError:=2
  548.        Else If (WordLength<5) Or (WordLength>8) Then PortOpenError:=3
  549.         Else If (StopBits<1) Or (StopBits>2) Then PortOpenError:=4
  550.          Else If Not(Parity In ['N','E','O','1','0']) Then PortOpenError:=5
  551.           Else If (ReceiveSize<2) Or (ReceiveSize>32767) Or (TransmitSize<2) Or (TransmitSize>32767)
  552.            Then PortOpenError:=6 Else Begin
  553.       ReleaseReceive:=False;
  554.       ReleaseTransmit:=False;
  555.       If ReceiveBuffer=Nil Then Begin
  556.         If MaxAvail<ReceiveSize Then PortOpenError:=7 Else Begin
  557.           GetMem(ReceiveBuffer,ReceiveSize);
  558.           ReleaseReceive:=True;
  559.         End;
  560.       End;
  561.       If TransmitBuffer=Nil Then Begin
  562.         If MaxAvail<TransmitSize Then PortOpenError:=7 Else ReleaseTransmit:=True;
  563.       End;
  564.       If ReleaseReceive Then Begin
  565.         FreeMem(ReceiveBuffer,ReceiveSize);
  566.         ReceiveBuffer:=Nil;
  567.       End;
  568.      End;
  569.      If (PortOpenError=0) And ((Port[BasePort+IIR] And $F8)<>0) Then PortOpenError:=8;
  570.      If PortOpenError=0 Then Begin { Get buffers }
  571.        If ReceiveBuffer=Nil Then GetMem(ReceiveBuffer,ReceiveSize);
  572.        ReceiveHead:=0;
  573.        ReceiveTail:=0;
  574.        If TransmitBuffer=Nil Then GetMem(TransmitBuffer,TransmitSize);
  575.        TransmitHead:=0;
  576.        TransmitTail:=0;
  577.        BaudDivisor:=BaudRateDividend Div BaudRate; { Set baud rate }
  578.        Port[BasePort+LCR]:=$80;
  579.        Port[BasePort+DLM]:=Hi(BaudDivisor);
  580.        Port[BasePort+DLL]:=Lo(BaudDivisor);
  581.        Work:=WordLength-5; { Set Word Length, Stop Bits, Parity }
  582. {       If StopBits=2 Then Work:=Work Or $04; }
  583. {       Case Parity Of
  584.          'N' :;
  585.          'O' :Work:=Work Or $08;
  586.          'E' :Work:=Work Or $18;
  587.          '1' :Work:=Work Or $28;
  588.          '0' :Work:=Work Or $38;
  589.        End; }
  590.        Port[BasePort+LCR]:=Work;
  591.        LineStatus:=Port[BasePort+LSR]; { Read registers to reset pending conditions }
  592.        ModemStatus:=Port[BasePort+MSR];
  593.        Work:=Port[BasePort+RBR];
  594.        AwaitingXon:=False;
  595.        AwaitingCh:=True;
  596.        SenderEnabled:=True;
  597.        FreeSave:=0; { Set interrupts }
  598.        VectorIndex:=1;
  599.        While (VectorIndex<=MaxPorts) And
  600.         ((VectorSave[VectorIndex].UseCount=0) Or (VectorSave[VectorIndex].IntrptNo<>IntNumber)) Do Begin
  601.          If (FreeSave=0) And (VectorSave[VectorIndex].UseCount=0) Then FreeSave:=VectorIndex;
  602.          Inc(VectorIndex);
  603.        End;
  604.        If VectorIndex<=MaxPorts Then With VectorSave[VectorIndex] Do Begin
  605.          DisableInterrupts;
  606.          Inc(UseCount);
  607.          PortList[UseCount]:=ComPort;
  608.        End Else If FreeSave=0 Then PortOpenError:=9{ This should never happen }
  609.         Else With VectorSave[FreeSave] Do Begin{ Save old vector }
  610.           VectorIndex:=FreeSave;
  611.           UseCount:=1;
  612.           PortList[1]:=ComPort;
  613.           IntrptNo:=IntNumber;
  614.           GetIntVec(IntrptNo,Vector);
  615.           Case VectorIndex Of
  616.             1:SetIntVec(IntrptNo,@AsyncISR1);
  617.             2:SetIntVec(IntrptNo,@AsyncISR2);
  618.             3:SetIntVec(IntrptNo,@AsyncISR3);
  619.             4:SetIntVec(IntrptNo,@AsyncISR4);
  620.            Else PortOpenError:=9;{ This shouldn't happen }
  621.           End;
  622.           Port[$21]:=Port[$21] And Not(1 Shl IrqNumber);
  623.         End;
  624.        PortOpen:=True;
  625.        Port[BasePort+MCR]:=MCRout2+MCRrts+MCRdtr;
  626.        Port[BasePort+IER]:=$0F;{ Enable 8250 interrupts }
  627.        EnableInterrupts;
  628.        AwaitingCts:=WaitForCts And ((ModemStatus And MSRcts)=0);
  629.        AwaitingDsr:=WaitForDsr And ((ModemStatus And MSRdsr)=0);
  630.        AwaitingCd:=WaitForCd And ((ModemStatus And MSRcd)=0);
  631.        TransmitEnabled:=Not(AwaitingCts Or AwaitingDsr Or AwaitingCd);
  632.      End;
  633.      OpenPort:=PortOpen And (PortOpenError=0);
  634.     End;
  635.   End;
  636.  
  637.   Procedure setparam(Comp,baud:word;parity:boolean);
  638.  
  639.   Begin
  640.     comport:=comp;
  641.     If Not openport(comport,baud,8,1,'N') Then Begin
  642.       WriteLn('Error opening Com',comport,' at ',baud,' baud');
  643.       Halt(9);
  644.     End;
  645.   End;
  646.  
  647.   Function carrier:Boolean;
  648.   Begin
  649.     carrier:=(Port[BasePorts[Comport]+$06] And 128)=128;
  650.   End;
  651.  
  652.   Procedure Sendchar(Ch:Char);
  653.   Var Timer:LongInt;
  654.       next:Word;
  655.   Begin
  656.   totalsent:=totalsent+1;
  657.     With AsyncPort[ComPort] Do Begin
  658.       Timer:=TimeoutMilliseconds;
  659.       next:=Succ(TransmitHead);
  660.       If next=TransmitSize Then next:=0;
  661.       While (next=TransmitTail) And (Timer>0) Do Begin
  662.         Delay(1);
  663.         Dec(Timer);
  664.       End;
  665.       If Timer>0 Then Begin
  666.         DisableInterrupts;
  667.         If TransmitEnabled And AwaitingCh Then Begin
  668.           Port[BasePort+THR]:=Ord(Ch);
  669.           AwaitingCh:=False;
  670.         End Else Begin
  671.           TransmitBuffer^[TransmitHead]:=Ord(Ch);
  672.           TransmitHead:=next;
  673.         End;
  674.         EnableInterrupts;
  675.       End Else Begin
  676.         LineStatus:=LSRTimeout;
  677.         If ErrorRoutine<>Nil Then CallFar(ComPort,ErrorRoutine);
  678.       End;
  679.     End;
  680.   End;
  681.  
  682.   Function getchar:Char; { Returns received character from buffer }
  683.   Var ch:Char;
  684.       bufused:Word;
  685.       Rch:Byte Absolute Ch;
  686.   Begin
  687.     With AsyncPort[ComPort] Do Begin
  688.       If ReceiveHead=ReceiveTail Then Else Begin
  689.         Rch:=ReceiveBuffer^[ReceiveTail];
  690.         DisableInterrupts;
  691.         Inc(ReceiveTail);
  692.         If ReceiveTail=ReceiveSize Then ReceiveTail:=0;
  693.         EnableInterrupts;
  694.         getchar:=ch;
  695.         totalrece:=totalrece+1;
  696.       End;
  697.       If Not SenderEnabled And (XoHand Or RtsHand Or DtrHand) Then Begin
  698.         DisableInterrupts;
  699.         If ReceiveHead<ReceiveTail Then bufused:=(ReceiveSize-ReceiveTail)+ReceiveHead
  700.          Else bufused:=ReceiveHead-ReceiveTail;
  701.         EnableInterrupts;
  702.         If bufused<(ReceiveSize Shr 1) Then EnableSender;
  703.       End;
  704.     End;
  705.   End;
  706.  
  707.   Procedure AsyncExit; { Exit procedure, close ports }
  708.   Var i:Word;
  709.   Begin
  710.     For i:=1 To MaxPorts Do ClosePort;
  711.     ExitProc:=FormerExitProc;
  712.   End;
  713.  
  714.   Procedure InitializeAsync; { Initialize data areas and install exit proc }
  715.   Var i:Word;
  716.   Begin
  717.     For i:=1 To MaxPorts Do Begin
  718.       With AsyncPort[i] Do Begin
  719.         PortOpen:=False;
  720.         IrqNumber:=IRQs[i];
  721.         IntNumber:=Interrupts[i];
  722.         VectorIndex:=0;
  723.         BasePort:=BasePorts[i];
  724.         LineStatus:=0;
  725.         ModemStatus:=0;
  726.         UserData:=0;
  727.         WaitForXon:=true; {false;} {False;} {True}
  728.         WaitForCts:=true; {false;} {false}{True}
  729.         WaitForDsr:=false; {False;} {True}
  730.         WaitForCd:=False;
  731.         RtsHand:=false; {False;} {True}
  732.         DtrHand:=True;
  733.         XoHand:=False;
  734.         XoTransparent:=true;
  735.         TransmitEnabled:=True;
  736.         AwaitingXon:=False;
  737.         AwaitingCts:=False;
  738.         AwaitingDsr:=False;
  739.         AwaitingCd:=False;
  740.         AwaitingCh:=True;
  741.         SenderEnabled:=True;
  742.         StreamInsert:=0;
  743.         ErrorRoutine:=Nil;
  744.         ModemRoutine:=Nil;
  745.         ReceiveBuffer:=Nil;
  746.         ReceiveSize:=DefaultBufferSize;
  747.         ReceiveHead:=0;
  748.         ReceiveTail:=0;
  749.         TransmitBuffer:=Nil;
  750.         TransmitSize:=DefaultBufferSize;
  751.         TransmitHead:=0;
  752.         TransmitTail:=0;
  753.         ReleaseReceive:=True;
  754.         ReleaseTransmit:=True;
  755.       End;
  756.       With VectorSave[i] Do Begin
  757.         UseCount:=0;
  758.         IntrptNo:=0;
  759.         Vector:=Nil;
  760.         NextPort:=1;
  761.       End;
  762.     End;
  763.     PortOpenError:=0;
  764.     FormerExitProc:=ExitProc;
  765.     ExitProc:=@AsyncExit;
  766.   End;
  767.  
  768.  
  769. End.
  770.